using UnityEngine;
using System.Collections;
using System.Collections.Generic;

namespace Botanicraft
{
    public class Tree2D
    {
        public GameObject treeObject;
        public GameObject leavesObject;
        public int id;

        public Texture2D bark;
        public Texture2D barkNormal;
        public Texture2D leaf;

        private List<Volume2D> volumes;
        public List<Leaf2D> leaves;
        public List<Branch2D> branches;

        public float growDist;
        public float minDist;
        public float maxDist;
        public uint maxNeighbors;
        public float thickness;
        public int meshSubdivisions;

        public bool finished;
        float iteration = -1;

        public Tree2D(int treeType, Vector3 position, int id, Texture2D barkTexture, Texture2D barkNormal, Texture2D leafTexture, float growDist = 1f, float maxDist = 2f, float minDist = 1.5f)
        {
            this.growDist = growDist;
            this.maxDist = maxDist;
            this.minDist = minDist;
            this.bark = barkTexture;
            this.barkNormal = barkNormal;
            this.leaf = leafTexture;
            finished = false;
            treeObject = new GameObject("Tree");
            leavesObject = new GameObject("Leaves");
            this.id = id;
            leaves = new List<Leaf2D>();
            branches = new List<Branch2D>();

            switch (treeType)
            {
                case 1: // Acacia
                    // Parameters
                    growDist = 1f;
                    maxDist = 2f;
                    minDist = 1.5f;
                    thickness = 0.025f;
                    meshSubdivisions = 6;
                    // Trunk
                    float tmp = Time.realtimeSinceStartup;
                    Volume2D trunk = new Volume2D(position);
                    trunk.leafNumber = 50;
                    trunk.ellipseRadius = new Vector3(0.5f, 2f, 0.5f);
                    //trunk.GenerateLeavesEllipsoid(ref rLeaves);
                    trunk.GenerateLeavesEllipse(ref leaves);
                    // Crowns
                    Volume2D crownMid = new Volume2D();
                    crownMid.leafNumber = 1000;
                    crownMid.ellipseRadius = new Vector3(10f, 4f, 4f);
                    crownMid.ellipseDensity = 1f;
                    crownMid.centerPosition = new Vector3(position.x + Random.Range(-1f, 1f), position.y + trunk.ellipseRadius.y + crownMid.ellipseRadius.y + Random.Range(-0.5f, 0.5f), position.z + Random.Range(-2f, 2f));
                    //crownMid.GenerateLeavesEllipsoid(ref rLeaves);
                    crownMid.GenerateLeavesEllipse(ref leaves);
                    int crownNumber = Random.Range(4, 8);
                    for (int i = 0; i < crownNumber; i++)
                    {
                        Volume2D crown = new Volume2D();
                        crown.leafNumber = 500;
                        crown.ellipseRadius = new Vector3(8f, 2f, 4f);
                        crown.ellipseOffsetY = new Vector2(0f, 1f);
                        crown.ellipseDensity = 0.75f;
                        crown.centerPosition = new Vector3(position.x + i * 4 + Random.Range(-12f, -10f), position.y + trunk.ellipseRadius.y + crown.ellipseRadius.y + Random.Range(7f, 9f) - 1.5f * Mathf.Abs((crownNumber / 2 - i)), position.z + Random.Range(-7f, 7f));
                        //crown.GenerateLeavesEllipsoid(ref rLeaves);
                        crown.GenerateLeavesEllipse(ref leaves);
                    }
                    float renderTime = (Time.realtimeSinceStartup - tmp);
                    //Debug.Log("Time for Volume: " + renderTime.ToString("f6"));
                    // Generation
                    PlaceSeed(trunk);
                    //Debug.Log("Total Leaves Begin: " + rLeaves.Count);
                    rGrow();
                    //Debug.Log("Total Leaves End: " + rLeaves.Count);
                    break;
                case 2: // Test bush Brute
                    // Parameters
                    growDist = 1f;
                    maxDist = 2f;
                    minDist = 1.5f;
                    thickness = 0.035f;
                    meshSubdivisions = 6;
                    // Crown
                    Volume2D bushB = new Volume2D();
                    bushB.leafNumber = 2000;
                    bushB.ellipseRadius = new Vector3(5f, 5f, 5f);
                    bushB.ellipseDensity = 1f;
                    bushB.centerPosition = new Vector3(0f, 0f, 0f);
                    bushB.GenerateLeavesEllipse(ref leaves);
                    PlaceSeed(bushB);
                    //ShowVolume(bush, maxDist, minDist);
                    //Debug.Log("Total Leaves Begin: " + leaves.Count);
                    rGrow();
                    //Debug.Log("Total Leaves End: " + leaves.Count);
                    break;
            }
        }

        public Tree2D(List<Leaf2D> leaves)
        {

        }

        public void rGrow()
        {
            //Debug.Log("Total Leaves: " + leaves.Count);
            if (!finished)
            {
                for (int i = 0; i < leaves.Count; i++)
                {
                    Leaf2D leaf = leaves[i];
                    float lowestDist = maxDist;
                    Vector3 lowestDir = Vector3.zero;
                    Branch2D closestBranch = null;
                    for (int j = 0; j < branches.Count; j++)
                    {
                        Branch2D branch = branches[j];
                        Vector3 dir = leaf.location - branch.location;
                        float dist = Vector3.Distance(leaf.location, branch.location);
                        if (dist > maxDist)
                        {
                            continue;
                        }
                        if (dist < minDist)
                        {
                            leaves.Remove(leaf);
                        }
                        if (closestBranch == null || dist <= lowestDist)
                        {
                            closestBranch = branch;
                        }
                        if (lowestDir == Vector3.zero || dist <= lowestDist)
                        {
                            lowestDir = dir;
                        }
                        if (dist <= lowestDist)
                        {
                            lowestDist = dist;
                        }
                    }
                    // We have the closest branch to that leaf
                    if (closestBranch != null)
                    {
                        closestBranch.growCount++;
                        lowestDir.Normalize();
                        closestBranch.growDir += lowestDir;
                    }
                }

                finished = true;
                for (int i = branches.Count - 1; i > 0; i--)
                {
                    Branch2D branch = branches[i];
                    branch.Draw(thickness);
                    if (branch.growCount > 0)
                    {
                        finished = false;
                        branch.growDir /= branch.growCount;
                        branch.growDir.Normalize();
                        branch.growDir *= growDist;
                        Branch2D newBranch = new Branch2D(branch.location + branch.growDir, branch, treeObject.transform, growDist, meshSubdivisions);
                        branches.Add(newBranch);

                        branch.growDir = new Vector3(0.0f, 0.0f, 0.0f);
                        branch.growCount = 0;
                        branch.area = 0.0f;
                    }
                }
                if (!finished)
                    rGrow();
                else
                    CreateMesh();
            }
        }

        public void CreateMesh()
        {
            for (int j = branches.Count - 1; j > 0; j--)
            {
                Branch2D branch = branches[j];
                branch.FinalizeSegment();
                branch.CreateSpheres(meshSubdivisions);
                if (branch.finalArea < 0.11f)
                    branch.DrawLeaves(leavesObject.transform, 7f);
            }

            MeshFilter[] treeMeshFilters = treeObject.GetComponentsInChildren<MeshFilter>();
            CombineInstance[] treeCombine = new CombineInstance[treeMeshFilters.Length];
            int i = 0;
            while (i < treeMeshFilters.Length)
            {
                treeCombine[i].mesh = treeMeshFilters[i].sharedMesh;
                treeCombine[i].transform = treeMeshFilters[i].transform.localToWorldMatrix;
                treeMeshFilters[i].gameObject.active = false;
                i++;
            }
            treeObject.AddComponent<MeshFilter>();
            treeObject.AddComponent<MeshRenderer>();
            treeObject.transform.GetComponent<MeshFilter>().mesh = new Mesh();
            treeObject.transform.GetComponent<MeshFilter>().mesh.CombineMeshes(treeCombine);
            //treeObject.transform.GetComponent<MeshFilter>().mesh.Optimize();

            MeshFilter[] leavesMeshFilters = leavesObject.GetComponentsInChildren<MeshFilter>();
            CombineInstance[] leavesCombine = new CombineInstance[leavesMeshFilters.Length];
            int k = 0;
            while (k < leavesMeshFilters.Length)
            {
                leavesCombine[k].mesh = leavesMeshFilters[k].sharedMesh;
                leavesCombine[k].transform = leavesMeshFilters[k].transform.localToWorldMatrix;
                leavesMeshFilters[k].gameObject.active = false;
                k++;
            }
            leavesObject.AddComponent<MeshFilter>();
            leavesObject.AddComponent<MeshRenderer>();
            leavesObject.transform.GetComponent<MeshFilter>().mesh = new Mesh();
            leavesObject.transform.GetComponent<MeshFilter>().mesh.CombineMeshes(leavesCombine);

            leavesObject.GetComponent<MeshRenderer>().material.shader = Shader.Find("Nature/Tree Soft Occlusion Leaves");
            leavesObject.GetComponent<MeshRenderer>().material.SetTexture("_MainTex", leaf as Texture);
            leavesObject.GetComponent<MeshRenderer>().material.SetFloat("_Cutoff", 0.25f);
            leavesObject.transform.parent = treeObject.transform;

            treeObject.GetComponent<MeshRenderer>().material.shader = Shader.Find("Nature/ats Soft Occlusion Bark");
            treeObject.GetComponent<MeshRenderer>().material.SetTexture("_MainTex", bark as Texture);
            treeObject.GetComponent<MeshRenderer>().material.SetTexture("_BumpMap", barkNormal as Texture);
            //treeObject.transform.GetComponent<MeshFilter>().mesh.subMeshCount = 1;
        }

        /// <summary>
        /// Creates the two necessary branch seeds to start growing the tree
        /// </summary>
        /// <param name="closestVolume">The volume that represents the base of the tree</param>
        public void PlaceSeed(Volume2D closestVolume)
        {
            branches.Add(new Branch2D(new Vector3(closestVolume.centerPosition.x, closestVolume.centerPosition.y - closestVolume.ellipseRadius.y - growDist, closestVolume.centerPosition.z), null, treeObject.transform, growDist, meshSubdivisions));
            branches.Add(new Branch2D(new Vector3(closestVolume.centerPosition.x, closestVolume.centerPosition.y - closestVolume.ellipseRadius.y, closestVolume.centerPosition.z), branches[branches.Count - 1], treeObject.transform, growDist, meshSubdivisions));
        }
    }
}
